-- Haskore Live!
--
-- David Siegel <djsiegel@seas.upenn.edu>
-- Jonathan Coveney <jcoveney@wharton.upenn.edu>
--
-- Beats.hs
--

-- | Module 'Beats' provides helpful functions for creating Music types by
-- parsing strings representing beats.
module Beats ( music
             , runTests
             ) where

import Test.HUnit

import Ratio
import Basics
import Data.Ratio

-- | 'Time' is the tempo of a beat.
type Time = Ratio Int

-- | 'BeatString' is simply a string whose characters are interpreted by this
-- module as Music atoms.
type BeatString = String

-- | Default beat used for chars not mapped.
dBeat :: Music
dBeat = qnr

-- | Default eigth-note beat.
eBeat :: Music
eBeat = perc AcousticSnare en []

-- | Default quarter-note beat.
qBeat :: Music
qBeat = perc AcousticSnare qn []


-- | Map from characters to Music. Used to convert a string of beats (e.g.
-- "f-f-fff-f-f-fff") to a Music.
beatMap :: [(Char, Music)]
beatMap = [ ('-', qnr)
          , ('o', qBeat)
          , ('f', eBeat :+: enr)
          , ('b', eBeat :+: eBeat)
          , ('l', enr :+: eBeat)
          ]

-- | Looks up a Char in the beat map. Provides a default Music value if Char is
-- not mapped.
charToMusic :: Char -> Music
charToMusic c = maybe dBeat id (lookup c beatMap)

testCharToMusic = TestCase $ do
    charToMusic '-' @?= qnr
    charToMusic '|' @?= dBeat

-- | Like 'charToMusic', but works on String.
stringToMusic s = foldr1 (:+:) (map charToMusic s)

testStringToMusic = TestCase $ do
    stringToMusic "-o-o" @?= (qnr :+: qBeat :+: qnr :+: qBeat)

-- | Given an instrument, tempo, and string of beats, creates a Music. This
-- function is simply an abbreviation for a nested constructor.
music :: IName -> Time -> BeatString -> Music
music i t bs = Tempo t (Instr i (stringToMusic bs))

testMusic = TestCase $ do
    music "Drums" 2 "o" @?= (Tempo 2 (Instr "Drums" qBeat))

-- | Runs unit tests.
runTests = runTestTT $
    TestList [ TestLabel "testCharToMusic" testCharToMusic
             , TestLabel "testStringToMusic" testStringToMusic
             , TestLabel "testMusic" testMusic
             ]
